package com.hemerasolutions.v2trainer.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.stereotype.Repository;

import com.hemerasolutions.v2trainer.dao.interfaces.ExerciseDaoInterface;
import com.hemerasolutions.v2trainer.model.exercise.Exercise;
import com.hemerasolutions.v2trainer.model.exercise.ExerciseMuscle;
import com.hemerasolutions.v2trainer.model.exercise.ExerciseMuscle_;
import com.hemerasolutions.v2trainer.model.exercise.Exercise_;
import com.hemerasolutions.v2trainer.model.exercise.executions.Execution;
import com.hemerasolutions.v2trainer.model.muscle.MuscleGroup_;
import com.hemerasolutions.v2trainer.model.muscle.Muscle_;
import com.hemerasolutions.v2trainer.model.library.Library;
import com.hemerasolutions.v2trainer.model.modality.Modality;
import com.hemerasolutions.v2trainer.model.modality.Modality_;
import com.hemerasolutions.v2trainer.model.modality.VersionableModality;
import com.hemerasolutions.v2trainer.model.modality.VersionableModality_;
import com.hemerasolutions.v2trainer.model.muscle.BodyRegion;
import com.hemerasolutions.v2trainer.model.muscle.Muscle;
import com.hemerasolutions.v2trainer.model.muscle.MuscleGroup;
import com.hemerasolutions.v2trainer.model.training.TrainingType;

@Repository(value = "exerciseDao")
public class ExerciseDao extends BaseDao<Exercise> implements ExerciseDaoInterface {

	private static final long serialVersionUID = 1L;

	@SuppressWarnings("unchecked")
	@Override
	public List<Exercise> search(Map<String, Object> fields) {

		List<Predicate> predicateList = new ArrayList<Predicate>();

		CriteriaBuilder cb = this.getEntityManager().getCriteriaBuilder();

		CriteriaQuery<Exercise> cq  = cb.createQuery(Exercise.class);
		
		Root<Exercise> exercise = cq.from(Exercise.class);

		Join<Exercise,Modality> joinModality = null;
		
		Join<Exercise,Execution> joinExecution = null;
		
		Join<Exercise,ExerciseMuscle> joinExerciseMuscle = null;

		Join<ExerciseMuscle,Muscle> joinMuscle = null;

		Join<Muscle,MuscleGroup> joinMuscleGroup = null;
		
		Predicate pred;
		
	    for (Map.Entry<String,Object> line : fields.entrySet()) {
	    	
	    	
	    	if( line.getKey().toString() == "name"){
	    		pred = cb.like(cb.upper(exercise.get(Exercise_.name)), "%"+line.getValue().toString().toUpperCase()+"%");
	    		predicateList.add(pred);    
	    		
	    	}else if (line.getKey().toString() == "description"){
	    		pred = cb.like(cb.upper(exercise.get(Exercise_.description)), "%"+line.getValue().toString().toUpperCase()+"%");
	    		predicateList.add(pred);    
	    		
	    	}else if( line.getKey().toString() == "trainingTypes" ){

	    		if(joinModality == null){
	    			joinModality = exercise.join(Exercise_.modality,JoinType.LEFT);
	    		}
	    			    		
	    		List<TrainingType> tts =  (List<TrainingType>)line.getValue();
	    		Expression<TrainingType> tt = joinModality.get(Modality_.trainingType);
	    		pred = tt.in(tts);
                predicateList.add(pred);    
	    			    		
	    		
	    	}else if( line.getKey().toString() == "modalitys" ){
	    		List<Modality> mods =  (List<Modality>)line.getValue();

	    		Expression<Modality> mod = exercise.get(Exercise_.modality);
	    		pred = mod.in(mods);
                predicateList.add(pred);    
	    			    		
	    			    		
	    	}else if( line.getKey().toString() == "executions" ){

	    		if(joinExecution == null){
	    			joinExecution = exercise.join(Exercise_.executions,JoinType.LEFT);
	    		}
	    		
	    		List<Execution> execs =  (List<Execution>)line.getValue();

	    		pred = joinExecution.in(execs);
                predicateList.add(pred);    	    		
	    	
	    	}else if( line.getKey().toString() == "muscles" ){
	    		
	    		if(joinExerciseMuscle == null){
	    			joinExerciseMuscle = exercise.join(Exercise_.exerciseMuscles,JoinType.LEFT);
	    		}
	    		
	    		if(joinMuscle == null){
	    			joinMuscle = joinExerciseMuscle.join(ExerciseMuscle_.muscle,JoinType.LEFT);
	    		}

	    		
	    		
	    		List<Muscle> muscs =  (List<Muscle>)line.getValue();

	    		pred = joinExerciseMuscle.in(muscs);
                predicateList.add(pred);    
	    		
	    	}else if( line.getKey().toString() == "muscleGroups" ){

	    		if(joinExerciseMuscle == null){
	    			joinExerciseMuscle = exercise.join(Exercise_.exerciseMuscles,JoinType.LEFT);
	    		}
	    		
	    		if(joinMuscle == null){
	    			joinMuscle = joinExerciseMuscle.join(ExerciseMuscle_.muscle,JoinType.LEFT);
	    		}
	    		
	    		List<MuscleGroup> mgs =  (List<MuscleGroup>)line.getValue();

	    		Expression<MuscleGroup> mg = joinMuscle.get(Muscle_.muscleGroup);

	    		pred = mg.in(mgs);
                predicateList.add(pred);    	    		

	    	}else if( line.getKey().toString() == "bodyRegions" ){

	    		if(joinExerciseMuscle == null){
	    			joinExerciseMuscle = exercise.join(Exercise_.exerciseMuscles,JoinType.LEFT);
	    		}
	    		
	    		if(joinMuscle == null){
	    			joinMuscle = joinExerciseMuscle.join(ExerciseMuscle_.muscle,JoinType.LEFT);
	    		}
	    		
	    		if(joinMuscleGroup == null){
	    			joinMuscleGroup = joinMuscle.join(Muscle_.muscleGroup,JoinType.LEFT);
	    		}
	    		
	    		
	    		List<BodyRegion> brs =  (List<BodyRegion>)line.getValue();

	    		Expression<BodyRegion> br = joinMuscleGroup.get(MuscleGroup_.bodyRegion);

	    		pred = br.in(brs);
                predicateList.add(pred);        		
	    	
	    	}else if( line.getKey().toString() == "librarys" ){

	    		if(joinModality == null){
	    			joinModality = exercise.join(Exercise_.modality,JoinType.LEFT);
	    		}
	    		
	    		List<Library> libs = (List<Library>)line.getValue();
	    		
	    		Path<VersionableModality> vm =  joinModality.get(Modality_.versionableModality);
	    		
	    		Expression<Library> lib =  vm.get(VersionableModality_.library);
	    		
	    		pred = lib.in(libs);
	    		predicateList.add(pred);    		

	    	}else if( line.getKey().toString() == "technical"){ 
	    		pred = cb.equal(exercise.get(Exercise_.technical), line.getValue());
	    		predicateList.add(pred);    		
	       	}	    	
	    	
	    }
	    
	    Predicate[] predicates = new Predicate[predicateList.size()];
	    predicateList.toArray(predicates);
	    cq.where(predicates);
	 
	    
	    return super.getEntityManager().createQuery(cq).getResultList();				
	
	}

}
